# -*- coding:utf-8 -*-
import joblib
import os
import numpy as np
from numpy import linalg as LA
import time
from sko.GA import GA

from preprocess_data import get_data

class PCAPROB(object):
	"""docstring for PCAPROB"""
	"""相对原生实现，不用pipeline"""
	def __init__(self,):
		super(PCAPROB, self).__init__()
		self.n_components = 150
		self.range = 1  # [-range,range] 在这个范围内找
		self.units = 5 # 每个特征被分为units份 两个端点独自一份
	def _num_to_index(self,num):
		if num > self.range:
			num = self.range
		if num < -self.range:
			num = -self.range
		index = int((num + self.range)/(2*self.range)*self.units) + 1
		if num == -self.range: index = 0
		return index
	def _num_to_index2(self,num): # num 为矩阵形式
		num[num>self.range] = self.range
		num[num<-self.range] = -self.range
		index = ((num + self.range)/(2*self.range)*self.units + 1).astype(np.int32)
		index[num==-self.range] = 0
		return index

	def train(self):
		train_features,train_labels = get_data('训练集')
		# train_features -> [N , F],train_labels -> [N]
		train_features_mean = train_features.mean(axis = 0,keepdims = True)
		train_features_std = train_features.std(axis = 0,keepdims = True)
		train_features = (train_features - train_features_mean)/(train_features_std+1e-8) # 利用广播机制

		##### pca ######
		# 求协方差矩阵
		corr = np.cov(train_features.T)
		# 求特征值和特征向量-已经按照特征值大 -> 小排好
		w, v = LA.eig(corr)
		train_features = np.matmul(train_features,v[:,:self.n_components])

		##### 特征值统计 ######
		features_count = np.zeros((self.n_components,200,self.units+2))
		# [F, C, U]-> 每个特征在每个区间的个数(F:features_num,C:class_num,U:units)
		# self.units + 2 是因为两个端点都考虑成一个区间
		# 每个样本
		for i in range(train_features.shape[0]):
			feature = train_features[i]
			label = train_labels[i]-1
			for j in range(self.n_components):
				features_count[j,label,self._num_to_index(feature[j])] += 1

		# # 考虑到两个原因
		# # 1. 如果一个统计区间仅有很少量的权重，应尽量减少权重
		# # 2. 每个特征相当于独立判断的因子，都有独自的权重必要时需要失活，对权重丢弃
		# ###### 训练分类器 #######
		# train_num = 1000 # 拿出训练集前100个样本进行训练
		# self.weight = np.ones((self.n_components,1)) # 这里先选用全部特征
		# labels = np.zeros((train_num),dtype = np.int32)
		# def demo_func(weight):
		# 	for i in range(train_num):
		# 		distinguish = features_count[np.arange(self.n_components),:,self._num_to_index2(train_features[i])]
		# 		distinguish *= np.expand_dims(np.array(weight),1)
		# 		prob = distinguish.sum(axis = 0)
		# 		labels[i] = np.argmax(prob)
		# 	return -np.sum(labels==(train_labels[:train_num]-1))/train_num
		
		# t = time.time()
		# g = GA(func=demo_func,n_dim = self.n_components, lb=[0]*self.n_components, ub=[1]*self.n_components, max_iter=30,precision=1)
		# best_x, best_y = g.fit()
		# print(best_x)
		# print(best_y)
		# print(time.time()-t)
		weight = [1]*self.n_components
		test_features,test_labels = get_data('测试集')
		test_features = (test_features - train_features_mean)/(train_features_std+1e-8) # 利用广播机制
		test_features = np.matmul(test_features,v[:,:self.n_components])
		labels = np.zeros((test_features.shape[0]),dtype = np.int32)
		for i in range(test_features.shape[0]):
			distinguish = features_count[np.arange(self.n_components),:,self._num_to_index2(test_features[i])]
			distinguish *= np.expand_dims(np.array(weight),1)
			prob = distinguish.sum(axis = 0)
			labels[i] = np.argmax(prob)
		print('test_acc=',np.sum(labels==(test_labels-1))/test_features.shape[0])






